﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Basmus.Tests
{
    public static class ElementsHelper
    {
        public static IEnumerable<IEnumerable<int>> GetElementsOfInt()
        {
            yield return new[] {0, 1, 4, 6, -1, -100000323};
            yield return new[] {9879, 22, 22};
            yield return new [] {7};
            yield return new[] { -12, 23, 234, 2, 23 };
            yield return new int[] {};
            yield return null;
        }

        public static IEnumerable<IEnumerable<bool>> GetElementsOfBoolean()
        {
            yield return new[] {true, false, false, true};
            yield return new[] { true };
            yield return new[] { false };
            yield return new bool[] {};
            yield return null;
        }

        public static IEnumerable<IEnumerable<TestEnum>> GetElementsOfTestEnum()
        {
            yield return new[] { TestEnum.Item4, TestEnum.Item1, TestEnum.Item4, TestEnum.Item1, TestEnum.Item2 };
            yield return new TestEnum[] { TestEnum.Item4 };
            yield return new TestEnum[] { TestEnum.Item3 };
            yield return new TestEnum[] { TestEnum.Item1 };
            yield return new TestEnum[] {};
            yield return null;
        }

        

        public static IEnumerable<IEnumerable<Guid>> GetElementsOfGuid()
        {
            yield return new[] { Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid() };
            yield return new[] { Guid.NewGuid() };
            yield return new Guid[] { };
            yield return null;
        }

        public static IEnumerable<IEnumerable<string>> GetElementsOfString()
        {
            yield return new[] { "asdfj", string.Empty, null };
            yield return new[] { "bar", "foo", "bar" };
            yield return new[] { "foo" };
            yield return new[] { string.Empty };
            yield return new string[] { null };
            yield return new string[] {};
            yield return null;
        }
        public static IEnumerable<IEnumerable<T?>> GetNullableElelementsOf<T>()
            where T : struct
        {
            var nullableElelementsOf = GetElementsOf<T>().Select(x => x == null ? null : x.Select(y => (T?) y));
            var startWithNull = nullableElelementsOf.Where(x => x != null).Select(x => new T?[] {null}.Concat(x));
            var endWithNull = nullableElelementsOf.Where(x => x != null).Select(x => x.Concat(new T?[] {null}));
            return nullableElelementsOf.Concat(startWithNull).Concat(endWithNull);
        }

        public static IEnumerable<IEnumerable<T>> GetElementsOf<T>()
        {
            var type = typeof (T);
            IEnumerable<IEnumerable<T>> result = null;
            if (type == typeof(int))
            {
                result = GetElementsOfInt().Cast<IEnumerable<T>>();
            }
            if (type == typeof(bool))
            {
                result= GetElementsOfBoolean().Cast<IEnumerable<T>>();
            }
            if (type == typeof(TestEnum))
            {
                result = GetElementsOfTestEnum().Cast<IEnumerable<T>>();
            }
            if (type == typeof(Guid))
            {
                result = GetElementsOfGuid().Cast<IEnumerable<T>>();
            }
            if (type == typeof(string))
            {
                result = GetElementsOfString().Cast<IEnumerable<T>>();
            }
            var underlyingType = Nullable.GetUnderlyingType(typeof (T));
            if (underlyingType != null)
            {
                result = (IEnumerable<IEnumerable<T>>) typeof (ElementsHelper)
                                                           .GetMethod("GetNullableElelementsOf")
                                                           .MakeGenericMethod(underlyingType)
                                                           .Invoke(null, null);
            }
            if (result != null)
            {
                return result;
            }
            //исключение - возникает когда для заднного типа не заданы наборы данных))
            throw new AssertFailedException();
        }
    }
}
